//////////////////////////////////////////////////////////////////////////
// PeekPocket - the hassle-free WiFi scanner for Windows Mobile
// coded by dzolee
// http://dzolee.blogspot.com
//
// ScannerDlg - this is the AP list tab
//////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "winuser.h"
#include "PeekPocket.h"
#include "ScannerDlg.h"
#include "PPDlg.h"
#include "SettingsStorage.h"
#include "TextCache.h"
#include "DataStorage.h"

extern CSettingsStorage g_Settings;
extern CTextCache g_Texts;
extern CDataStorage g_APData;
extern CWifiPeek g_WifiPeek;

extern bool g_bPopupMenuActive;

IMPLEMENT_DYNAMIC(CScannerDlg, CColoredDlg)

//////////////////////////////////////////////////////////////////////////
CScannerDlg::CScannerDlg(CWnd* pParent /*=NULL*/)
	: CColoredDlg(CScannerDlg::IDD, pParent)
{
	m_bListFontApplied=false;
	m_numactive=_T("");
	m_rb_static=_T("");
	m_ViewMode=vmNormal;
}

//////////////////////////////////////////////////////////////////////////
CScannerDlg::~CScannerDlg()
{
}

//////////////////////////////////////////////////////////////////////////
void CScannerDlg::DoDataExchange(CDataExchange* pDX)
{
	CColoredDlg::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_DEVICE_LIST, m_list);
	DDX_Text(pDX, IDC_NUM_ACTIVE, m_numactive);
	DDX_Text(pDX, IDC_RB_STATIC, m_rb_static);
	DDX_Control(pDX, IDC_OSCOPE, m_OScope);
}


BEGIN_MESSAGE_MAP(CScannerDlg, CColoredDlg)
	ON_WM_PAINT()
	ON_COMMAND(ID_POPUP_GOLARGE, &CScannerDlg::OnPopupGolarge)
	ON_COMMAND(ID_POPUP_HISTORY, &CScannerDlg::OnPopupHistory)
	ON_COMMAND(ID_POPUP_CONNECT, &CScannerDlg::OnPopupConnect)
	ON_COMMAND(ID_POPUP_DISCONNECT, &CScannerDlg::OnPopupDisconnect)
	ON_WM_LBUTTONDOWN()
	ON_NOTIFY(LVN_GETDISPINFO, IDC_DEVICE_LIST, &CScannerDlg::OnLvnGetdispinfoList)
	ON_NOTIFY(NM_CLICK, IDC_DEVICE_LIST, &CScannerDlg::OnNMClickDeviceList)
END_MESSAGE_MAP()

// CWifiDevicesDlg message handlers
//////////////////////////////////////////////////////////////////////////
BOOL CScannerDlg::OnInitDialog()
{
CRect rcDlg, rcList;
CPPDlg *pParent;
DWORD dwTemp;

	CColoredDlg::OnInitDialog();

	//set up list columns
	m_list.InsertColumn(0, g_Texts.GetString(teName), LVCFMT_LEFT, 50);
	m_list.InsertColumn(1, g_Texts.GetString(teSig), LVCFMT_LEFT, 35);
	m_list.InsertColumn(2, g_Texts.GetString(teCh), LVCFMT_LEFT, 30);
	m_list.InsertColumn(3, g_Texts.GetString(teType), LVCFMT_LEFT, 40);
	m_list.InsertColumn(4, g_Texts.GetString(teSec), LVCFMT_LEFT, 30);
	m_list.InsertColumn(5, g_Texts.GetString(teMAC), LVCFMT_LEFT, 80);
	m_list.InsertColumn(6, g_Texts.GetString(teBestSig), LVCFMT_LEFT, 40);
	m_list.InsertColumn(7, g_Texts.GetString(teWorstSig), LVCFMT_LEFT, 40);
	m_list.InsertColumn(8, g_Texts.GetString(teFirstSeen), LVCFMT_LEFT, 100);
	m_list.InsertColumn(9, g_Texts.GetString(teLastSeen), LVCFMT_LEFT, 100);

	//resize list
	pParent=(CPPDlg *)GetParent();
	if(pParent != NULL)
	{
		//size to fit to tab control
		pParent->m_tab.GetWindowRect(&rcDlg);
		m_list.GetWindowRect(&rcList);
		rcList.SetRect(0, 0, rcDlg.Width(), rcList.Height());

		m_list.MoveWindow(&rcList);

		//OScope control will have the same dimensions
		m_OScope.MoveWindow(&rcList);
	}

	g_Settings.GetNumeric(nseFontSize, dwTemp);
	if(dwTemp < 10 || dwTemp > 20)
	{
		dwTemp = 12;
	}
	SetListFont(dwTemp);
	m_rb_static=g_Texts.GetString(teTapAPName);
	
	//init OScope
	
	//not required because the control is in the .rc file!
	//m_OScope.Create(WS_VISIBLE, CRect(100, 100, 150, 150), this, IDC_OSCOPE);

	m_OScope.ShowWindow(SW_HIDE);
	// Customize the control
	m_OScope.SetRange(-100.0, 0.0, 1);
	m_OScope.SetYUnits(_T("dBm"));
	m_OScope.SetBackgroundColor(RGB_AVERAGE(GetSysColor(COLOR_ACTIVECAPTION), RGB(255, 255, 255)));
	m_OScope.SetGridColor(RGB(0, 0, 0));
	m_OScope.SetPlotColor(RGB(0, 0, 0));
	UpdateData(FALSE);
	return TRUE;
}

//////////////////////////////////////////////////////////////////////////
void CScannerDlg::OnPaint()
{
	CColoredDlg::OnPaint();
	m_list.ShowWindow(SW_HIDE);
	m_OScope.ShowWindow(SW_HIDE);
	ApplyListFont();


	//by xlos
	//display AP data with huge font	
	
	CFont font;
	HGDIOBJ hOldObj;
	CRect rc;
	CString str;
	DisplayInfo di;
	CDC *pDC=GetDC();
	font.CreateFont(20, 0, 0, 0, FW_BOLD, FALSE /*italic*/, FALSE /*underline*/, 0, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, _T("Tahoma")) ;
	hOldObj=pDC->SelectObject(font);

	GPS_POSITION pos = g_WifiPeek.m_GPSSink.GetGPSPosition();
	char buff[1024];
	sprintf(buff, "%lf %lf", pos.dblLatitude, pos.dblLongitude);
	
	//str=buff;
	str=_T("hello world");

	//let the dialog setup colors in DC
	SendMessage(WM_CTLCOLORSTATIC, (WPARAM)pDC->GetSafeHdc(), -1);

	//display signal
	GetClientRect(&rc);
	rc.top+=20;
	rc.bottom-=20;

	pDC->DrawText(str, rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
	pDC->SelectObject(hOldObj);

	//display AP name
	font.DeleteObject();
	ReleaseDC(pDC);
	return;
	//by xlos end


	switch(m_ViewMode)
	{
		case vmNormal:
		{
			m_list.ShowWindow(SW_SHOW);
			break;
		}
		case vmHistory:
		{
/*
			SYSTEMTIME st;
			double dVal;

			GetSystemTime(&st);
			dVal=(double)-2*(60-st.wSecond);
			m_OScope.AppendPoint(dVal);
			m_OScope.ShowWindow(SW_SHOW);
			break;
*/
			//advance history graph
			DisplayInfo di;
			if(true == g_APData.GetItemByID(m_iSelectedID, &di))
			{
				//if active, append signal strength to graph
				if(di.bActive == true)
				{
					m_OScope.AppendPoint(di.RSSI);
				}
				else
					//otherwise, append minimum signal
				{
					m_OScope.AppendPoint(-100);
				}

			}
			m_OScope.ShowWindow(SW_SHOW);
			break;
		}
		case vmLarge:
		{
			//display AP data with huge font
			CFont font;
			HGDIOBJ hOldObj;
			CRect rc;
			CString str;
			DisplayInfo di;

			CDC *pDC=GetDC();
			if(pDC != NULL)
			{				
				font.CreateFont(100, 0, 0, 0, FW_BOLD, FALSE /*italic*/, FALSE /*underline*/, 0, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, _T("Tahoma")) ;
				hOldObj=pDC->SelectObject(font);

				str=_T("--");

				//get row
				g_APData.GetItemByID(m_iSelectedID, &di);				

				//only if active
				if(di.bActive == true)
				{
					str.Format(_T("%d"), abs(di.RSSI));
				}

				//let the dialog setup colors in DC
				SendMessage(WM_CTLCOLORSTATIC, (WPARAM)pDC->GetSafeHdc(), -1);

				//display signal
				GetClientRect(&rc);
				rc.top+=20;
				rc.bottom-=20;

				pDC->DrawText(str, rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
				pDC->SelectObject(hOldObj);

				//display AP name
				font.DeleteObject();
				font.CreateFont(20, 0, 0, 0, FW_BOLD, FALSE /*italic*/, FALSE /*underline*/, 0, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, _T("Tahoma")) ;
				hOldObj=pDC->SelectObject(font);

				GetClientRect(&rc);
				rc.bottom=rc.top+20;

				str=di.SSID;

				pDC->DrawText(str, rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
				pDC->SelectObject(hOldObj);

				ReleaseDC(pDC);
			}//if DC ok
			break;
		}//case vmLarge
	}//switch ViewMode
}

//////////////////////////////////////////////////////////////////////////
//internal function to apply changed list font and resize columns if required
void CScannerDlg::ApplyListFont()
{
LVCOLUMN col;

	if(m_bListFontApplied == false)
	{
		col.mask = LVCF_WIDTH;

		//resize columns
		col.cx=(int) (50 * (m_iListFontSize/10.0));
		m_list.SetColumn(0, &col);

		col.cx=(int) (10 + (35 * (m_iListFontSize/14.0)));
		m_list.SetColumn(1, &col);

		col.cx=(int) (30 * (m_iListFontSize/10.0));
		m_list.SetColumn(2, &col);

		col.cx=(int) (40 * (m_iListFontSize/10.0));
		m_list.SetColumn(3, &col);

		col.cx=(int) (35 * (m_iListFontSize/10.0));
		m_list.SetColumn(4, &col);

		col.cx=(int) (90 * (m_iListFontSize/10.0));
		m_list.SetColumn(5, &col);

		col.cx=(int) (40 * (m_iListFontSize/10.0));
		m_list.SetColumn(6, &col);

		col.cx=(int) (40 * (m_iListFontSize/10.0));
		m_list.SetColumn(7, &col);

		col.cx=(int) (100 * (m_iListFontSize/10.0));
		m_list.SetColumn(8, &col);

		col.cx=(int) (100 * (m_iListFontSize/10.0));
		m_list.SetColumn(9, &col);

		//set list font
		CWnd* pWnd=GetDlgItem(IDC_DEVICE_LIST);
		if(pWnd != NULL)
		{
			//no repaint
			pWnd->SetFont(NULL, FALSE);
			m_listfont.DeleteObject();
			m_listfont.CreateFont(m_iListFontSize, 0, 0, 0, FW_BOLD, FALSE /*italic*/, FALSE /*underline*/, 0, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, _T("Tahoma")) ;
			//do repaint
			pWnd->SetFont(&m_listfont);
		}
		m_bListFontApplied=true;
	}	
	//font will be deleted by dialog dtor
}

//////////////////////////////////////////////////////////////////////////
//this function will make sure the list font gets updated
void CScannerDlg::SetListFont(int iFontSize)
{
	//if size differs from current
	if(m_iListFontSize != iFontSize)
	{
		m_iListFontSize=iFontSize;
		//need to set on next activate
		m_bListFontApplied=false;
	}
}

//////////////////////////////////////////////////////////////////////////
//handle go large menu click
void CScannerDlg::OnPopupGolarge()
{
	//enter "myopic" mode
	m_ViewMode=vmLarge;
	m_rb_static=g_Texts.GetString(teTapToRestore);
	m_list.ShowWindow(SW_HIDE);
	m_OScope.ShowWindow(SW_HIDE);
	UpdateData(FALSE);
}

//////////////////////////////////////////////////////////////////////////
//handle history menu click
void CScannerDlg::OnPopupHistory()
{
	//enter chart mode
	m_ViewMode=vmHistory;
	m_rb_static=g_Texts.GetString(teTapToRestore);
	m_list.ShowWindow(SW_HIDE);
	//clear chart
	m_OScope.Reset();
	UpdateData(FALSE);
}


//////////////////////////////////////////////////////////////////////////
//tap handler - used to come back from large mode
void CScannerDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
	//if in large mode, return to normal
	if(m_ViewMode != vmNormal)
	{
		ReturnToNormalMode();
	}
#ifdef _DEBUG
	else
	{
		OnPopupHistory();
	}
#endif
	Default();
}

//////////////////////////////////////////////////////////////////////////
void CScannerDlg::ReturnToNormalMode()
{
	m_ViewMode=vmNormal;
	m_rb_static=g_Texts.GetString(teTapAPName);
	m_list.DeleteAllItems();
	m_list.ShowWindow(SW_SHOW);


	UpdateData(FALSE);
}

//////////////////////////////////////////////////////////////////////////
void CScannerDlg::UpdateList()
{
DWORD dwTotal, dwActive, dwOldC;

	g_APData.GetCounts(dwTotal, dwActive);

	dwOldC=m_list.GetItemCount();

//	if(dwOldC != dwTotal)
	{
		m_list.SetItemCount(dwTotal);
	}

	m_numactive.Format(_T("%d/%d "), dwActive, dwTotal);
	m_numactive+=g_Texts.GetString(teActive);
	UpdateData(FALSE);

	//force repaint if large
	if(m_ViewMode != vmNormal)
	{
		Invalidate();
	}
}

//////////////////////////////////////////////////////////////////////////
void CScannerDlg::OnLvnGetdispinfoList(NMHDR *pNMHDR, LRESULT *pResult)
{
NMLVDISPINFO *pDispInfo;
LV_ITEM *pItem;
CString str;
DisplayInfo di;

	pDispInfo=reinterpret_cast<NMLVDISPINFO*>(pNMHDR);

	pItem= &(pDispInfo)->item;

#ifdef _DEBUG
	if(pItem->iSubItem == 0)
	{
		CString str;
		str.Format(_T("GetDispInfo: requested row %d\n"), pItem->iItem);
		OutputDebugString(str);
	}
#endif

	//text needed?
	if(pItem->mask & LVIF_TEXT)
	{
		g_APData.GetFilteredItemByIndex(pItem->iItem, &di);
		switch(pItem->iSubItem)
		{
			case 0:	//name
			{
				str=di.SSID;
				break;
			}
			case 1:	//sig
			{
				if(di.bActive == true)
				{
					str.Format(_T("%d"), abs(di.RSSI));
				}
				else
				{
					str=_T("-----");
				}
				break;
			}
			case 2:	//ch
			{
				str.Format(_T("%d"), di.Channel);
				break;
			}
			case 3:	//type
			{
				switch(di.Infastructure)
				{
					case Ndis802_11IBSS:
					{
						str=g_Texts.GetString(tePeer);
						break;
					}
					case Ndis802_11Infrastructure:
					{
						str=g_Texts.GetString(teAP);
						break;
					}
					default:
						str=_T("?");
				}
				break;
			}
			case 4:	//sec
			{
				str=_T("?");

				switch(di.Auth)
				{
					case Ndis802_11AuthModeOpen:
					{
						str=g_Texts.GetString(teOpen);
						break;
					}
					case Ndis802_11AuthModeWPA_XP:
					{
						//this is the same in all languages so far..
						str=_T("WPA");
						break;
					}
					case Ndis802_11AuthModeWPA2_XP:
					{
						//this is the same in all languages so far..
						str=_T("WPA2");
						break;
					}
					default:
					{
						//this is the same in all languages so far..
						str=_T("WEP");
					}
				}//switch di.auth
				break;
			}//case 4
			case 5:	//MAC
			{
				str.Format(_T("%02x-%02x-%02x-%02x-%02x-%02x"), di.BSSID[0], di.BSSID[1], di.BSSID[2], di.BSSID[3], di.BSSID[4], di.BSSID[5]);
				break;
			}
			case 6:	//Best
			{
				str.Format(_T("%d"), di.iBestSig);
				break;
			}
			case 7:	//Worst
			{
				str.Format(_T("%d"), di.iWorstSig);
				break;
			}
			case 8:	//First seen
			{
				COleDateTime dFirst(di.dFirstSeen);

				//could use COleDateTime::Format
				str.Format(_T("%02d/%02d/%02d %02d:%02d:%02d"), dFirst.GetYear() % 1000, dFirst.GetMonth(), dFirst.GetDay(), dFirst.GetHour(), dFirst.GetMinute(), dFirst.GetSecond());
				break;
			}
			case 9:	//Last seen
			{
				//last seen
				COleDateTime dLast(di.dLastSeen);

				str.Format(_T("%02d/%02d/%02d %02d:%02d:%02d"), dLast.GetYear() % 1000, dLast.GetMonth(), dLast.GetDay(), dLast.GetHour(), dLast.GetMinute(), dLast.GetSecond());
				break;
			}
#ifdef _DEBUG
			default:
			OutputDebugString(_T("Unhandled list subitem\n"));
#endif
		}
		//return text
		_tcsncpy(pItem->pszText, str, pItem->cchTextMax);
	}
	*pResult = 0;
}

//////////////////////////////////////////////////////////////////////////
void CScannerDlg::OnNMClickDeviceList(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMLISTVIEW lplv;
DisplayInfo di;

	lplv=(LPNMLISTVIEW)pNMHDR;

	if(lplv->iItem != -1)
	{
		CMenu popup, *subitem;
		CPoint pt;

		if(FALSE != popup.LoadMenu(IDR_MENU_LIST_POPUP))
		{
			subitem = popup.GetSubMenu(0);

			//update menu item text - must delete & insert since there is no direct API for this
			subitem->DeleteMenu(0, MF_BYPOSITION);
			subitem->InsertMenu(0, MF_BYPOSITION, ID_POPUP_GOLARGE, g_Texts.GetString(teGoLarge));

			subitem->DeleteMenu(1, MF_BYPOSITION);
			subitem->InsertMenu(1, MF_BYPOSITION, ID_POPUP_HISTORY, g_Texts.GetString(teHistory));

			//delete Connect and Disconnect menus for now
			subitem->DeleteMenu(ID_POPUP_CONNECT, MF_BYCOMMAND);
			subitem->DeleteMenu(ID_POPUP_DISCONNECT, MF_BYCOMMAND);

			/*
			subitem->DeleteMenu(2, MF_BYPOSITION);
			subitem->InsertMenu(2, MF_BYPOSITION, ID_POPUP_CONNECT, g_Texts.GetString(teConnect));

			subitem->DeleteMenu(3, MF_BYPOSITION);
			subitem->InsertMenu(3, MF_BYPOSITION, ID_POPUP_DISCONNECT, g_Texts.GetString(teDisconnect));
			*/

			//get info about clicked AP
			g_APData.GetFilteredItemByIndex(lplv->iItem, &di);

			//store clicked item *ID*
			m_iSelectedID=di.ID;

/*
			//if not connected to the currently selected AP, remove "Disconnect" menu
			BYTE CurrentMac[6];

			//zeroinit struct
			memset((void *)CurrentMac, 0, sizeof(CurrentMac));
			CString strCurrentAdapter;

			//get MAC add of preferred AP
			g_Settings.GetString(sseAdapter, strCurrentAdapter);
			g_WifiPeek.GetPreferredMACAddress(strCurrentAdapter.GetBuffer(), &CurrentMac[0]);

			if(memcmp((void *)CurrentMac, (void *)di.BSSID, 6) == 0)
			{
				//logmsg(_T("%s: user clicked currently connected AP name."));

				//remove "Connect" menu item because this AP is already connected!
				subitem->DeleteMenu(2, MF_BYPOSITION);
			}
			else
			{
				//logmsg(_T("%s: not clicked a connected AP name."));
				//remove "Disconnect" menu item.
				subitem->DeleteMenu(3, MF_BYPOSITION);

				//if AP is not open..
				if(di.Auth != Ndis802_11AuthModeOpen)
				{
					//remove "Connect" menu item because this AP uses WEP!
					subitem->DeleteMenu(2, MF_BYPOSITION);
				}
			}
*/

			//convert coordinates
			pt=lplv->ptAction;
			ClientToScreen(&pt);

			//disable timer restart for CPPDlg::OnTimer
			g_bPopupMenuActive=true;

			//display popup menu
			subitem->TrackPopupMenu(TPM_LEFTALIGN, pt.x, pt.y, this);

			//re-enable timer restart for CPPDlg::OnTimer
			g_bPopupMenuActive=false;
		}//if menu load ok
		else
		{
#ifdef _DEBUG
			OutputDebugString(_T("Failed to load popup menu!\n"));
#endif
		}
	}//if item not -1
	*pResult=0;
}

//////////////////////////////////////////////////////////////////////////
//handle "Connect" menu click
void CScannerDlg::OnPopupConnect()
{
	//first disconnect
	OnPopupDisconnect();

	//set infrastructure mode, set privacy, disable encryption..
	CString strCurrentAdapter, strAP;

	g_Settings.GetString(sseAdapter, strCurrentAdapter);

	DisplayInfo di;
	//get AP info struct - clicked ID was saved when popup menu was displayed!
	if(true == g_APData.GetItemByID(m_iSelectedID, &di))
	{
		//set to peer or infrastructure depending on connected item type
		g_WifiPeek.PrepareToConnect(strCurrentAdapter.GetBuffer(), di.Infastructure);

		g_WifiPeek.ConnectToAPName(strCurrentAdapter.GetBuffer(), di.SSID);
		g_WifiPeek.ConnectToMACAddress(strCurrentAdapter.GetBuffer(), di.BSSID);

		//renew DHCP ip address
		g_WifiPeek.RenewIpAddress(strCurrentAdapter.GetBuffer());
	}
}

//////////////////////////////////////////////////////////////////////////
//handle "Disconnect" menu click
void CScannerDlg::OnPopupDisconnect()
{
CString strCurrentAdapter;

	g_Settings.GetString(sseAdapter, strCurrentAdapter);
	g_WifiPeek.Disconnect(strCurrentAdapter.GetBuffer());
}
